#include"including.h"
#pragma once
using namespace std;

namespace enawful
{
    namespace enunderlined{
        string input_file_name;
        string output_file_name;

        string thisline;
        vector<string> linkmap;
        vector<int> code;
        string thisword="";

        enum word_type{
            Number,
            Operator,
            String,
            Char,
            Identifier,
            Break,
            Error_type
        };
        word_type thisword_type;
        bool __argument__fill_the_line=false;
        int __argument__fill_length=-1;
        bool _need_generate_output_name=true;

        namespace enunderlined_basecommends{
            void print_help(){
                cout<<
                    "enunderlined by zhengjiabao"<<endl<<
                    "version 1.0.2"<<endl<<
                    "turn your progrom into Fill The Blank,"<<endl<<
                    "And the output file IS ABLE TO compile!(smile)"<<endl<<
                    ""<<endl<<
                    "Usage:enawful enunderlined <input-file-name>  [options]"<<endl<<
                    ""<<endl<<
                    "Options"<<endl<<
                    "---------------------- -------------------------------------------"<<endl<<
                    "--fill-the-line        Use Notes led with `//` and many underlines"<<endl<<
                    "                       follows"<<endl<<
                    "--fill-length N        define the width of a line with N"<<endl<<
                    "--output-to FILENAME   Output to other file instead of *.output.cpp"<<endl;
                return;
            }
            void print_description(){
                cout<<"enunderlined by zhengjiabao"<<endl<<
                "version 1.0.2"<<endl<<
                "turn your progrom into Fill The Blank,"<<endl<<
                "And the output file IS ABLE TO compile!(smile)"<<endl<<
                ""<<endl<<
                "Note:"<<endl<<
                "Unable to transfer Note or Cross Line String."<<endl<<
                ""<<endl<<
                "For example:"<<endl<<
                "INPUT test.cpp"<<endl<<
                "```cpp"<<endl<<
                "#include<iostream>"<<endl<<
                "using namespace std;"<<endl<<
                "int main(){"<<endl<<
                "    int a,b;"<<endl<<
                "    cin>>a>>b;"<<endl<<
                "    cout<<a+b;"<<endl<<
                "    return 0;"<<endl<<
                "}"<<endl<<
                "```"<<endl<<
                "OUTPUT test.output.cpp"<<endl<<
                "```cpp"<<endl<<
                "#include<iostream>"<<endl<<
                "#define _ using"<<endl<<
                "#define __ namespace"<<endl<<
                "#define ___ std"<<endl<<
                "#define ____ ;"<<endl<<
                "#define _____ int"<<endl<<
                "#define ______ main"<<endl<<
                "#define _______ (){"<<endl<<
                "#define ________ a"<<endl<<
                "#define _________ ,"<<endl<<
                "#define __________ b"<<endl<<
                "#define ___________ cin"<<endl<<
                "#define ____________ >>"<<endl<<
                "#define _____________ cout"<<endl<<
                "#define ______________ <<"<<endl<<
                "#define _______________ +"<<endl<<
                "#define ________________ return"<<endl<<
                "#define _________________ 0"<<endl<<
                "#define __________________ }"<<endl<<
                "_"<<endl<<
                "__"<<endl<<
                "___"<<endl<<
                "____"<<endl<<
                "_____"<<endl<<
                "______"<<endl<<
                "_______"<<endl<<
                "_____"<<endl<<
                "________"<<endl<<
                "_________"<<endl<<
                "__________"<<endl<<
                "____"<<endl<<
                "___________"<<endl<<
                "____________"<<endl<<
                "________"<<endl<<
                "____________"<<endl<<
                "__________"<<endl<<
                "____"<<endl<<
                "_____________"<<endl<<
                "______________"<<endl<<
                "________"<<endl<<
                "_______________"<<endl<<
                "__________"<<endl<<
                "____"<<endl<<
                "________________"<<endl<<
                "_________________"<<endl<<
                "____"<<endl<<
                "__________________"<<endl<<
                "```";
                return ;
            }
        }

        bool is_operator(char _ch){
            if(
                _ch=='+'||
                _ch=='-'||
                _ch=='*'||
                _ch=='/'||
                _ch=='\\'||
                _ch=='('||
                _ch==')'||
                _ch=='{'||
                _ch=='}'||
                _ch=='['||
                _ch==']'||
                _ch==','||
                _ch=='.'||
                _ch=='?'||
                _ch==':'||
                _ch=='|'||
                _ch=='&'||
                _ch=='%'||
                _ch=='~'||
                _ch=='!'||
                _ch=='<'||
                _ch=='>'||
                _ch=='='||
                _ch==';'||
                _ch=='.'||
                _ch=='^'
            ){
                return true;
            }
            return false;
        }

        word_type meet_the_char(string line,size_t index){
            if(line[index]=='\"'){
                return String;
            }
            if(line[index]=='\''){
                return Char;
            }
            if(isdigit(line[index])){
                return Number;
            }
            if(isalpha(line[index])){
                return Identifier;
            }
            if(line[index]=='_'){
                return Identifier;
            }
            if(line[index]=='.'&&index<line.length()&&isdigit(line[index+1])){
                return Number;
            }
            if(is_operator(line[index])){
                return Operator;
            }
            if(line[index]==' '||line[index]=='\t'){
                return Break;
            }
            return Error_type;
        }

        void insert_to_list(string _sh){

            vector <string>::iterator position=find(linkmap.begin(),linkmap.end(),_sh);
            if(position==linkmap.end()){
                linkmap.push_back(_sh);
            }
            vector <string>::iterator position2=find(linkmap.begin(),linkmap.end(),_sh);
            code.push_back(position2-linkmap.begin()+1);
            /*
            cout<<"LOG:\""<<_sh<<"\" is trying to insert,code_in="<<position-linkmap.begin()<<endl;
            cout<<"code={";
            for(vector<int>::iterator _index=code.begin();_index!=code.end();_index++){
                cout<<*_index<<",";
            }
            cout<<"}\nlinkmap={";
            for(vector<string>::iterator _index=linkmap.begin();_index!=linkmap.end();_index++){
                cout<<*_index<<",";
            }
            cout<<"}\n";
            */
            thisword="";
            return;
        }

        void seperate_this_line(){
            for (size_t _i = 0; _i < thisline.length(); _i++)
            {
                if(thisword==""){
                    thisword_type=meet_the_char(thisline,_i);
                    if(thisword_type==Break){
                        continue;
                    }
                    thisword.push_back(thisline[_i]);
                    continue;
                }
                if(thisword_type==Char&&thisline[_i]=='\''){
                    
                    if(_i>=2&&thisline[_i-1]=='\\'&&thisline[_i-2]=='\\'){
                        thisword.push_back(thisline[_i]);
                        insert_to_list(thisword);
                        continue;
                    }

                    
                    if(_i>=1&&thisline[_i-1]=='\\'){
                        thisword.push_back(thisline[_i]);
                        continue;
                    }
                    thisword.push_back(thisline[_i]);
                    insert_to_list(thisword);
                    continue;
                }
                if(thisword_type==Char&&thisline[_i]!='\''){
                    thisword.push_back(thisline[_i]);
                    continue;
                }
                if(thisword_type!=String&&thisline[_i]=='/'&&thisline[_i-1]=='/'){
                    return;
                }
                if(thisword_type==String&&thisline[_i]=='\"'){
                    
                    if(_i>=2&&thisline[_i-1]=='\\'&&thisline[_i-2]=='\\'){
                        thisword.push_back(thisline[_i]);
                        insert_to_list(thisword);
                        continue;
                    }

                    
                    if(_i>=1&&thisline[_i-1]=='\\'){
                        thisword.push_back(thisline[_i]);
                        continue;
                    }

                    thisword.push_back(thisline[_i]);
                    insert_to_list(thisword);
                    continue;
                }
                if(thisword_type==String&&thisline[_i]!='\"'){
                    thisword.push_back(thisline[_i]);
                    continue;
                }
                if(thisword_type==Identifier&&meet_the_char(thisline,_i)!=Break&&meet_the_char(thisline,_i)!=Operator){
                    thisword.push_back(thisline[_i]);
                    continue;
                }
                if(thisword_type==Identifier&&meet_the_char(thisline,_i)==Break){
                    insert_to_list(thisword);
                    continue;
                }
                if(thisword_type==Identifier&&meet_the_char(thisline,_i)==Operator){
                    insert_to_list(thisword);
                    _i--;
                    continue;
                }
                if(thisword_type==Number&&meet_the_char(thisline,_i)==Number){
                    thisword.push_back(thisline[_i]);
                    continue;
                }
                if(thisword_type==Operator&&meet_the_char(thisline,_i)==Operator){
                    thisword.push_back(thisline[_i]);
                    continue;
                }
                if(thisword_type!=meet_the_char(thisline,_i)){
                    insert_to_list(thisword);
                    _i--;
                    continue;
                }
            }
            if(thisword!=""){
                insert_to_list(thisword);
            }
            return;

        }

        string generate_output_file_name(string input_file_name){
            size_t found_n=input_file_name.find_last_of(".");
            if(found_n==string::npos){
                throw runtime_error("Input file don't have a suffix");
            }else{
                return input_file_name.substr(0,found_n)+".output"+input_file_name.substr(found_n);
            }
        }

        

        int enunderlined(){
            if(_need_generate_output_name){
                output_file_name=generate_output_file_name(input_file_name);
            }
            
            cout<<"Output to:"<<output_file_name;

            ifstream input_file_stream((char*)input_file_name.data());
            ofstream output_file_stream((char*)output_file_name.data());

            while(input_file_stream.good()){
                getline(input_file_stream,thisline);
                
                if(thisline[0]=='#'){
                    output_file_stream<<thisline<<endl;
                    continue;
                }
                seperate_this_line();
            }

            for(vector<string>::iterator _index=linkmap.begin();_index!=linkmap.end();_index++){
                output_file_stream<<"#define ";
                for (size_t i = 0; i < _index-linkmap.begin()+1; i++)
                {
                    output_file_stream<<"_";
                }
                output_file_stream<<" "<<*_index<<endl;
            }

            output_file_stream<<endl;

            for(vector<int>::iterator _index=code.begin();_index!=code.end();_index++){
                for (size_t i = 0; i < *_index; i++)
                {
                    output_file_stream<<"_";
                }
                if(__argument__fill_the_line){
                    if(__argument__fill_length<linkmap.end()-linkmap.begin()){
                        __argument__fill_length=linkmap.end()-linkmap.begin();
                    }
                    output_file_stream<<"//";
                    for (size_t i = 0; i < __argument__fill_length-*_index; i++)
                    {
                        output_file_stream<<"_";
                    }
                    
                }
                output_file_stream<<endl;
            }

            return 0;
        }
        void separate_the_argument(int argc , char * argv[]){
            if(argc==2){
                enunderlined_basecommends::print_help();
                exit(0);
            }
            size_t i = 2;
            if(argv[2][0]!='-'){
                input_file_name=argv[2];
                i++;
            }
            for (; i < argc; i++)
            {
                if(strcmp(argv[i],"description")==0){
                    enunderlined_basecommends::print_description();
                    exit(0);
                }
                if(strcmp(argv[i],"help")==0){
                    enunderlined_basecommends::print_help();
                    exit(0);
                }
                if(strcmp(argv[i],"--input-file-name")==0){
                    if(i+1>argc){
                        cout<<"You have not input the value of `--input-file-name`.Type ...`help` for more information."<<endl;
                        exit(0);
                    }
                    input_file_name=argv[i+1];
                }
                
                if(strcmp(argv[i],"--fill-the-line")==0){
                    __argument__fill_the_line=true;
                }
                if(strcmp(argv[i],"--fill-length")==0){
                    if(i+1>argc){
                        cout<<"You have not input the length of `--fill-length`.This argument will be ignored.Type ...`help` for more information."<<endl;
                        continue;
                    }
                    __argument__fill_length=strtol(argv[i+1],(char**)NULL,10);
                    i++;
                }
                if(strcmp(argv[i],"--output-to")==0){
                    if(i+1>argc){
                        cout<<"You have not input the length of `--output-to`.This argument will be ignored.Type ...`help` for more information."<<endl;
                        continue;
                    }
                    output_file_name=argv[i+1];
                    _need_generate_output_name=false;
                    i++;
                }
                
            }
        }

        int main(int argc , char * argv[]){
            separate_the_argument(argc,argv);
            return enunderlined();
        }
    }
    
} 


